//-----------------------------------------------------------------------
// <copyright file="FolderShareSecurityTest.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY 
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
// </copyright>
// <author>Simon Bell</author>
// <email>v-sibell</email>
// <date>2004-06-01</date>
// <summary>Tests folder sharing with security options</summary>
//-----------------------------------------------------------------------

namespace Microsoft.Sdc.Tasks.Test
{
    using System;
    using System.Globalization;
    using System.IO;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using Microsoft.Sdc.Tasks.Configuration;
    using Microsoft.Sdc.Tasks;
    using Microsoft.Build.Framework;
    using Microsoft.Sdc.Tasks.Configuration.ActiveDirectory;

    using System.Management;
    using System.Collections;

    using System.Runtime.InteropServices;
    using System.Security.Principal;
    using System.Security.Permissions;


    /// <summary>
    /// Unit test for the create folder share
    /// </summary>
    [TestClass]
    public class FolderShareSecurityTest
    {
        [DllImport("advapi32.dll", CharSet = CharSet.Auto)]
        public static extern bool LogonUser(String lpszUserName, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        public extern static bool CloseHandle(IntPtr handle);

        

        private const string MACHINE_NAME = ".";
        private const string FOLDER_SHARE_DESCRIPTION = "Temporary share for unit testing";
        private const string FOLDER_SHARE_NAME = "UnitTest";
        private const string TEMP_PASSWORD = "1234$abcd";
        private const int LOGON32_LOGON_INTERACTIVE = 2;
        private const int LOGON32_PROVIDER_DEFAULT = 0;

        public FolderShareSecurityTest()
        {
        }

        
        [TestMethod]
        public void TestCreateFolderShareSecurity()
        {
            ArrayList tempUsers = new ArrayList();
            string allowedUsers = String.Empty;
            string deniedUsers = String.Empty;
            char[] splitter = { ';' };

            bool isAllowed = true;

            //Create two allowed users and two denied users for the newly created share
            for (int i = 0; i < 4; i++)
            {
                Tasks.ActiveDirectory.User.Create tempUser = new Tasks.ActiveDirectory.User.Create();
                tempUser.UserName = TaskTestUtilities.GenerateNewGuid().Replace("-", "").Substring(0, 11);
                tempUser.Password = TEMP_PASSWORD;
                Assert.IsTrue( tempUser.Execute());
                if (isAllowed)
                {
                    allowedUsers += Environment.MachineName + "\\" + tempUser.UserName + ";";
                }
                else
                {
                    deniedUsers += Environment.MachineName + "\\" + tempUser.UserName + ";";
                }
                isAllowed = !isAllowed;
                tempUsers.Add(tempUser);
            }

            //Set up task
            Tasks.Folder.Share.Create createFolderShareTask = new Tasks.Folder.Share.Create();
           
            createFolderShareTask.AllowedUsers = allowedUsers.Split(splitter,StringSplitOptions.RemoveEmptyEntries);
            createFolderShareTask.DeniedUsers = deniedUsers.Split(splitter, StringSplitOptions.RemoveEmptyEntries);

            Tasks.Folder.Share.DeleteShare deleteFolderShareTask = new Tasks.Folder.Share.DeleteShare();
            
            string tempDirectory = TaskTestUtilities.CreateTempFolder();

            try
            {
                //createFolderShareTask.Machine = MACHINE_NAME;
                createFolderShareTask.Path = tempDirectory;
                createFolderShareTask.Description = FOLDER_SHARE_DESCRIPTION;
                createFolderShareTask.ShareName = FOLDER_SHARE_NAME;

                int maximumAllowed = 1;
                createFolderShareTask.MaximumAllowed = 1;

                //NB - if machine name is "." or Environment.MachineName, then you can't 
                //set the username/password for the task, or you'll generate a
                //  "User credentials cannot be used for local connections"
                //exception. 

                bool createTaskReturnValue = createFolderShareTask.Execute();

                //Test the folder share was created
                Assert.IsTrue(createTaskReturnValue);

                //NB: This fails, as tempDirectory is in MSDOS form, whilst .Path is fully expanded.

//              if (createFolderShareTask.Path != tempDirectory)
//              {
//                  Console.WriteLine(createFolderShareTask.Path + " - " + tempDirectory);
//              }

                Assert.IsTrue(createFolderShareTask.Description == FOLDER_SHARE_DESCRIPTION);
                Assert.IsTrue(createFolderShareTask.ShareName == FOLDER_SHARE_NAME);
                Assert.IsTrue(createFolderShareTask.MaximumAllowed == maximumAllowed);

                //NB (S. Bell)
                //###
                //This section succeeds on all tested dev and daily servers. However, due to the 
                //querying of System.Management, it has been commented out for performance.

                //Ensure that this share really has been created using the System.Management namespace
//              PropertyDataCollection createdShareData = FindShare(createFolderShareTask);
//              Assertion.AssertNotNull("Failed to find shared folder after create task", createdShareData);
//
//              //Ensure the properties of the share were set correctly
//              PropertyDataCollection.PropertyDataEnumerator shareDataEnumerator = createdShareData.GetEnumerator();
//              while (shareDataEnumerator.MoveNext())
//              {
//                  PropertyData propertyData = (PropertyData) shareDataEnumerator.Current;
//                  if (propertyData.Name == "Name")
//                  {
//                      Assert.AreEqual("Created folder share name", propertyData.Value, createFolderShareTask.ShareName);
//                  }
//
//                  if (propertyData.Name == "Path")
//                  {
//                      Assert.AreEqual("Created folder share path", propertyData.Value, createFolderShareTask.Path);
//                  }
//
//                  if (propertyData.Name == "Description")
//                  {
//                      Assert.AreEqual("Created folder share description", propertyData.Value, createFolderShareTask.Description);
//                  }
//              }

                //Ensure that denied users cannot see this share

                //TODO:
                //CheckUserShareAccess(createFolderShareTask.AllowedUsers[0], true, createFolderShareTask);
                //CheckUserShareAccess(createFolderShareTask.DeniedUsers[0], false, createFolderShareTask);

                //Extend this to try to connect/disconnect from the share
//              Tasks.Folder.Share.Connect connectTask = new Microsoft.Sdc.Tasks.Configuration.Tasks.Folder.Share.Connect();
//              connectTask.BuildEngine = pseudoBuildEngine;
//
//              //Try to connect as an allowed user
//              connectTask.UserName = createFolderShareTask.AllowedUsers[0];
//              connectTask.Password = "1234$abcd";
//              connectTask.ShareName = FOLDER_SHARE_NAME;
//              connectTask.ResourceType = "Disk";
//
//              Console.WriteLine("Before connecting, there are " + connectTask.Connections + " connections");
//              bool connectTaskReturnValue = connectTask.Execute();
//
//              Assert.IsTrue("TaskCompletedSuccessfully", connectTaskReturnValue);
//              Console.WriteLine("After connecting, there are " + connectTask.Connections + " connections");
//
//              //And disconnect the same user
//              Tasks.Folder.Share.Disconnect disconnectTask = new Microsoft.Sdc.Tasks.Configuration.Tasks.Folder.Share.Disconnect();
//              disconnectTask.BuildEngine = pseudoBuildEngine;
//
//              //Try to connect as an allowed user
//              disconnectTask.UserName = createFolderShareTask.AllowedUsers[0];
//              disconnectTask.Password = "1234$abcd";
//              disconnectTask.ShareName = FOLDER_SHARE_NAME;
//              disconnectTask.Force = true;
//
//              bool disconnectTaskReturnValue = disconnectTask.Execute();
//
//              Assert.IsTrue("TaskCompletedSuccessfully", disconnectTaskReturnValue);

                //Delete the share
                //deleteFolderShareTask.Machine = MACHINE_NAME;
                deleteFolderShareTask.ShareName = FOLDER_SHARE_NAME;

                bool deleteTaskReturnValue = deleteFolderShareTask.Execute();

                //Test the folder share was deleted
                Assert.IsTrue(deleteTaskReturnValue);

                //Test the properties of the task
                Assert.IsTrue(deleteFolderShareTask.ShareName == FOLDER_SHARE_NAME);

                //Ensure that the share has been deleted
                // PropertyDataCollection deletedShareData = FindShare(deleteFolderShareTask);
                // Assertion.AssertNull("Found shared folder after delete task", deletedShareData);
            }
            finally
            {
                for (int i = 0; i < 4; i++)
                {
                    User user = User.Load(((Tasks.ActiveDirectory.User.Create) tempUsers[i]).UserName);
                    bool userDeleted = user.Delete();
                }

                //And the folder
                Directory.Delete(tempDirectory, true);

            }
        }

        private PropertyDataCollection FindShare(Microsoft.Sdc.Tasks.Folder.Share.ShareTaskBase task)
        {
            ConnectionOptions shareConnection = new ConnectionOptions();

            ManagementScope shareScope = new ManagementScope(@"\\" + "." + @"\Root\CIMv2", shareConnection);
            ObjectQuery shareQuery = new ObjectQuery("Select * from Win32_Share");
            ManagementObjectSearcher shareSearch = new ManagementObjectSearcher(shareScope, shareQuery);
            ManagementObjectCollection shareResults = shareSearch.Get();

            PropertyDataCollection sharePropertyData = null;

            foreach (ManagementObject searchObject in shareResults)
            {
                PropertyDataCollection searchResultProperties = searchObject.Properties;

                PropertyDataCollection.PropertyDataEnumerator propertyEnumerator = searchResultProperties.GetEnumerator();
                while (propertyEnumerator.MoveNext())
                {
                    PropertyData propertyData = (PropertyData) propertyEnumerator.Current;
                    if ((propertyData.Name == "Name") && (propertyData.Value.ToString() == task.ShareName))
                    {
                        sharePropertyData = searchResultProperties;
                    }
                }
            }
            return sharePropertyData;
        }

        private void ListLocalDomainUsers()
        {
            ConnectionOptions shareConnection = new ConnectionOptions();

            ManagementScope scope = new ManagementScope(@"\\" + "." + @"\Root\CIMv2", shareConnection);
            string queryString = "select * from Win32_Account where Domain='" + Environment.MachineName + "'";

            ObjectQuery shareQuery = new ObjectQuery(queryString);

            ObjectQuery query = new ObjectQuery(queryString);
            ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);

            ManagementObjectCollection coll = searcher.Get();
            if (coll != null)
            {
                Console.WriteLine("Objects found = " + coll.Count.ToString());
            }
            else
            {
                Console.WriteLine("Nothing back");
            }

            foreach (ManagementObject returnedAccount in searcher.Get())
            {
                Console.WriteLine(returnedAccount.ToString());
                PropertyDataCollection.PropertyDataEnumerator propertyEnumerator = returnedAccount.Properties.GetEnumerator();
                while (propertyEnumerator.MoveNext())
                {
                    Console.WriteLine("Found name:" + ((PropertyData) propertyEnumerator.Current).Name);
                }
            }

        }

        private void CheckUserShareAccess(string username, bool expectedSuccess, Microsoft.Sdc.Tasks.Folder.Share.ShareTaskBase task)
        {
            // Now switch context to this user
            IntPtr userHandle = new IntPtr(0);
            int index = username.LastIndexOf("\\");
            string shortUsername = username.Substring(index + 1, username.Length - (index + 1));
            Console.WriteLine("ShortUsername = " + shortUsername);

            bool returnValue = LogonUser(shortUsername,
                                            System.Environment.MachineName,
                                            TEMP_PASSWORD,
                                            LOGON32_LOGON_INTERACTIVE,
                                            LOGON32_PROVIDER_DEFAULT,
                                            ref userHandle);

            if (!returnValue)
            {
                throw new Exception("Invalid Username: " + shortUsername);
            }

            WindowsIdentity newId = new WindowsIdentity(userHandle);
            WindowsImpersonationContext impersonatedUser = newId.Impersonate();

            Console.WriteLine("Now running in context of : " + WindowsIdentity.GetCurrent().Name);

            try
            {
                PropertyDataCollection deniedShareData = FindShare(task);

                if ((deniedShareData == null) && (expectedSuccess))
                {
                    if (expectedSuccess)
                    {
                        throw new Exception("Can't see the share");
                    }
                    else
                    {
                        Console.WriteLine("Could not see share as expected");
                    }
                }
                else
                {
                    if (!(expectedSuccess))
                    {
                        throw new Exception("Share data found when user should be denied");
                    }
                }

            }
            catch (ApplicationException)
            {
                if (expectedSuccess)
                {
                    throw;
                }
                else
                {
                    Console.WriteLine("Correctly caught exception: " + ((PseudoBuildEngine) task.BuildEngine).PreviousMessage);
                }
            }
            finally
            {
                //Return to previous identity
                impersonatedUser.Undo();
                if (userHandle != IntPtr.Zero)
                {
                    CloseHandle(userHandle);
                }
            }
        }

    }
}


